package handle

import (
	"duanju-mq/cache"
	"duanju-mq/db"
	"duanju-mq/lib/firebase"
	"duanju-mq/pkg/app"
	"duanju-mq/pkg/config"
	"duanju-mq/pkg/log"
	"encoding/json"
	"fmt"
	"os"
	"sync"
)

var (
	serviceFile = *config.String("fcm.account", "./debug-firebase-service-account.json", "service_account.json")
	scopes      = []string{"https://www.googleapis.com/auth/firebase.messaging"}

	mu        sync.Mutex
	FCMConfig *firebase.Config
)

func init() {
	_ = loadServiceAccountJson()
	db.AfterDbOpen(func() {
		app.Async(func() {
			_, _ = GetAccessToken()
		})
	})
}

func GetAccessToken() (string, error) {
	token, err := cache.GetFCMAccessToken()
	if err != nil {
		log.Err(fmt.Sprintf("%v %+v", "", err))
	}

	if token != "" {
		return token, nil
	}

	mu.Lock()
	defer mu.Unlock()

	if token, _ = cache.GetFCMAccessToken(); token != "" {
		return token, nil
	}

	fcmAccessToken, err := getAccessTokenFromFCM()
	if err != nil {
		return "", err
	}

	if err = cache.SetFCMAccessTokenCache(fcmAccessToken.AccessToken, fcmAccessToken.Expiry.Unix()); err != nil {
		log.Err(fmt.Sprintf("cache.SetFCMAccessTokenCache %+v %+v", fcmAccessToken, err))
	}

	return fcmAccessToken.AccessToken, nil
}

func loadServiceAccountJson() error {
	jsonData, err := os.ReadFile(serviceFile)
	if err != nil {
		log.Err(fmt.Sprintf("os.ReadFile %v %+v", serviceFile, err))
		return err
	}

	FCMConfig = &firebase.Config{}
	if err = json.Unmarshal(jsonData, FCMConfig); err != nil {
		log.Err(fmt.Sprintf("json[%v] %+v", jsonData, err))
	}
	return err
}

func getAccessTokenFromFCM() (*firebase.Token, error) {
	if FCMConfig == nil {
		if err := loadServiceAccountJson(); err != nil {
			return nil, err
		}
	}

	authToken, err := firebase.GetAccessToken(FCMConfig, scopes...)
	if err != nil {
		return nil, err
	}

	return authToken, nil

}

// 推送
func HandleFCMPush(rawData string) {
	info := &firebase.Message{}
	if err := json.Unmarshal([]byte(rawData), info); err != nil {
		log.Err(fmt.Sprintf("%v %+v", rawData, err))
		return
	}

	push(info)
}

func HandleMsgPush(deviceId string, msg *firebase.Message) {

	if pushed := cache.IsPushed(deviceId); pushed {
		return
	}

	if err := push(msg); err == nil {
		log.Err(fmt.Sprintf("push %+v", err))
		return
	}

	_ = cache.SetPushed(deviceId)
}

func push(msg *firebase.Message) error {
RESTART:
	accessToken, err := GetAccessToken()
	if err != nil {
		return err
	}

	var statusCode int
	if statusCode, err = firebase.SendToToken(FCMConfig.ProjectId, accessToken, msg); err != nil {
		//401 UNAUTHENTICATED accessToken失效
		if statusCode == 401 {
			_ = cache.SetFCMAccessTokenCache("", -1)
			goto RESTART
		}
		return err
	}

	return nil
}
